﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.ComponentModel;
using System.Threading;
using System.Windows.Data;

using StopwatchApp.StateMachine;

namespace StopwatchApp
{
    public partial class MainPage : PhoneApplicationPage, INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Implementation
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string strPropertyName)
        {
            if (null != PropertyChanged)
                PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
        }
        #endregion

        #region Binding Properties
        private TimeSpan _TotalStopWatchDuration;
        public TimeSpan TotalStopWatchDuration
        {
            get { return _TotalStopWatchDuration; }
            set
            {
                _TotalStopWatchDuration = value;
                OnPropertyChanged("TotalStopWatchDuration");
            }
        }

        private TimeSpan _CurrentLapDuration;
        public TimeSpan CurrentLapDuration
        {
            get { return _CurrentLapDuration; }
            set
            {
                _CurrentLapDuration = value;
                OnPropertyChanged("CurrentLapDuration");
            }
        }

        private string _StartStopButtonText;
        public string StartStopButtonText
        {
            get { return _StartStopButtonText; }
            set
            {
                _StartStopButtonText = value;
                OnPropertyChanged("StartStopButtonText");
            }
        }

        private string _LapResetButtonText;
        public string LapResetButtonText
        {
            get { return _LapResetButtonText; }
            set
            {
                _LapResetButtonText = value;
                OnPropertyChanged("LapResetButtonText");
            }
        }

        private bool _LapResetButtonEnabled;
        public bool LapResetButtonEnabled
        {
            get { return _LapResetButtonEnabled; }
            set
            {
                _LapResetButtonEnabled = value;
                OnPropertyChanged("LapResetButtonEnabled");
            }
        }

        private ObservableCollection<LapData> _AllLaps;
        public ObservableCollection<LapData> AllLaps
        {
            get { return _AllLaps; }
            set
            {
                _AllLaps = value;
                OnPropertyChanged("AllLaps");
            }
        }
        #endregion

        Timer _Timer;
        StopwatchMachine _StopwatchMachine;

        // Constructor
        public MainPage()
        {
            InitializeComponent();

            // Pass in a function to be called when the state of the stopwatch changes.
            _StopwatchMachine = new StopwatchMachine(UpdateScreenBasedOnState);

            TotalStopWatchDuration = TimeSpan.MinValue;
            CurrentLapDuration = TimeSpan.MinValue;

            AllLaps = new ObservableCollection<LapData>();

            // Begin a timer to update the UI every 25ms w/ the current duration of the stopwatch.
            // TODO: figure out how pricy this is to run over the duration of the application.
            _Timer = new Timer((state) =>
                {
                    Dispatcher.BeginInvoke(() =>
                    {
                        TotalStopWatchDuration = _StopwatchMachine.FullDuration;
                        CurrentLapDuration = _StopwatchMachine.LapDuration;
                    });
                }, null, 0, 25);

        }

        /// <summary>
        /// Based on the given state, update the text and availability of buttons to the user.
        /// </summary>
        /// <param name="eState">Current state of the stopwatch.</param>
        private void UpdateScreenBasedOnState(StopwatchState eState)
        {
            switch (eState)
            {
                case StopwatchState.Empty:
                    StartStopButtonText = "Start";
                    LapResetButtonText = "Lap";
                    LapResetButtonEnabled = false;
                    break;
                case StopwatchState.Paused:
                    StartStopButtonText = "Start";
                    LapResetButtonText = "Reset";
                    LapResetButtonEnabled = true;
                    break;
                case StopwatchState.Timing:
                    StartStopButtonText = "Stop";
                    LapResetButtonText = "Lap";
                    LapResetButtonEnabled = true;
                    break;
            }
        }

        /// <summary>
        /// Say hello to my little state machine interface!!!
        /// </summary>
        /// <param name="sender">Used to determine which button was pressed.</param>
        /// <param name="e">Not used.</param>
        private void HandleStateChange(object sender, EventArgs e)
        {
            switch (_StopwatchMachine.State)
            {
                case StopwatchState.Empty:
                    if (_StartStopButton == sender)
                    {
                        _StopwatchMachine.StartStopwatch();
                    }
                    break;
                case StopwatchState.Paused:
                    if (_StartStopButton == sender)
                    {
                        _StopwatchMachine.StartStopwatch();
                    }
                    else if (_LapResetButton == sender)
                    {
                        _StopwatchMachine.ResetStopwatch();
                        AllLaps.Clear(); // Need to clear out all of the laps previously recorded.
                    }
                    break;
                case StopwatchState.Timing:
                    if (_StartStopButton == sender)
                    {
                        _StopwatchMachine.PauseStopwatch();
                    }
                    else if (_LapResetButton == sender)
                    {
                        _StopwatchMachine.LapStopwatch();

                        // Get all of the laps in descending order.
                        var allLapsInDescendingOrder = from aLap in _StopwatchMachine.Laps
                                                       orderby aLap.LapNumber descending
                                                       select aLap;
                        AllLaps = new ObservableCollection<LapData>(allLapsInDescendingOrder);
                    }
                    break;
            }
        }
    }

    /// <summary>
    /// Class used to autogenerate 30 laps for use in the designer.
    /// </summary>
    public class LapDataListDesignPresentation : List<LapData>
    {
        private const int REALLY_LARGE_NUMBER = 100000000;
        private static Random _Random = new Random();

        public LapDataListDesignPresentation AllLaps
        {
            get { return new LapDataListDesignPresentation(); }
        }

        protected TimeSpan RandomSpan
        {
            get { return new TimeSpan(_Random.Next(REALLY_LARGE_NUMBER)); }
        }

        public TimeSpan TotalStopWatchDuration { get { return RandomSpan; } }
        public TimeSpan CurrentLapDuration { get { return RandomSpan; } }

        public string StartStopButtonText { get { return "Start"; } }
        public string LapResetButtonText { get { return "Reset"; } }
        public bool LapResetButtonEnabled { get { return false; } }

        public LapDataListDesignPresentation()
        {
            //var aRandom = new Random((int)DateTime.Now.Ticks);
            for (int i = 0; i < 30; i++)
            {
                Add(new LapData(i, RandomSpan));
            }

            Reverse();
        }
    }

    /// <summary>
    /// Converts a TimeSpan to a string to display to users.
    /// </summary>
    public class TimeSpanToDisplayStringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if ((null != value) && (typeof(TimeSpan) == value.GetType()))
                return GetTimeSpanAsString((TimeSpan)value);
            return GetTimeSpanAsString(TimeSpan.MinValue);
        }

        private string GetTimeSpanAsString(TimeSpan aTimeSpan)
        {
            object[] stringParams = { aTimeSpan.Days,
            aTimeSpan.Hours,
            aTimeSpan.Minutes,
            aTimeSpan.Seconds,
            aTimeSpan.Milliseconds};

            if (0 == aTimeSpan.Days)
            {
                if (0 == aTimeSpan.Hours)
                {
                    return String.Format("{2:D2}:{3:D2}.{4:D3}", stringParams);
                }
                return String.Format("{1:D2}:{2:D2}:{3:D2}.{4:D3}", stringParams);
            }
            return String.Format("{0}.{1:D2}:{2:D2}:{3:D2}.{4:D3}", stringParams);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

}